#include <sys/socket.h>
#include <sys/types.h>
#include <stdlib.h>
#include "fifo.h"
#include <stdio.h>
#include <string.h>



/*
 * Funcao que calcula o endereço base para inserção
 */
short calcBs(short pos) {
	return(pos-1)*sizeof(tFifo)+6;
}

/*
 * funncao que calcula a posição que será inserido na fila
 */
short calcPos(short x) {
	return((x+sizeof(tFifo)-6)/sizeof(tFifo));
}

/*
 * funcao que adiciona um registro no inicio da fila
 */
void addInicio(tFifo aux, FILE *file, short pos) {
	tFifo nex;
	short ur, aux2;
	if (pos != 0) {
			
		aux2 = pos;
		aux2 = calcBs(pos);
		fseek(file, aux2, SEEK_SET);
		fread(&nex, sizeof(tFifo), 1, file);
		ur = nex.prox;
		fseek(file, 4, SEEK_SET);
		fwrite(&ur, sizeof(short), 1, file);
		fseek(file, aux2, SEEK_SET);
		fwrite(&aux, sizeof(tFifo), 1, file);
		fseek(file, 0, SEEK_SET);
		fwrite(&pos, sizeof(short), 1, file);
		fwrite(&pos, sizeof(short), 1, file);
		
	}else {
		fseek(file, 6, SEEK_SET);	
		fwrite(&aux, sizeof(tFifo), 1, file);
		aux2 = ftell(file) - sizeof(tFifo);
		aux2 = calcPos(aux2);
		fseek(file, 0, SEEK_SET);
		fwrite(&aux2, sizeof(short), 1, file);
		fseek(file, 2, SEEK_SET);
		fwrite(&aux2, sizeof(short), 1, file);	
	}
	
	
}

/*
 * funcao que adiciona um registro no fim da fila
 */
void addFinal(tFifo aux, FILE *file) {
	short x, aux2;	
	tFifo f;
	
	fseek(file, 0, SEEK_END);
	fwrite(&aux, sizeof(tFifo), 1, file);
	x = ftell(file) - sizeof(tFifo);
	x = calcPos(x);
	fseek(file, 2, SEEK_SET);
	fread(&aux2, sizeof(short), 1, file);
	fseek(file, 2, SEEK_SET);
	fwrite(&x, sizeof(short), 1, file);
	aux2 = calcBs(aux2);
	fseek(file, aux2, SEEK_SET);
	fread(&f, sizeof(tFifo), 1, file);
	f.prox = x;
	fseek(file, aux2, SEEK_SET);
	fwrite(&f, sizeof(tFifo), 1, file);
}

/*
 * funcao que adiciona um registro no meio da fila, a partir da posicao passada por parametro
 */
void addMeio(tFifo aux, short pos, FILE *file) {
	short x, y, u;
	tFifo ler;	
	x = calcBs(pos);
	fseek(file, x, SEEK_SET);
	fread(&ler, sizeof(tFifo), 1, file);
	u = ler.prox;
	fseek(file, x, SEEK_SET);
	fwrite(&aux, sizeof(tFifo), 1, file);
	fseek(file, 4, SEEK_SET);
	fwrite(&u, sizeof(short), 1, file);
	fseek(file, 2, SEEK_SET);
	fread(&y, sizeof(short), 1, file);
	y = calcBs(y);
	fseek(file, 2, SEEK_SET);
	fwrite(&pos, sizeof(short), 1, file);
	fseek(file, y, SEEK_SET);
	fread(&ler, sizeof(tFifo), 1, file);
	ler.prox = pos;
	fseek(file, y, SEEK_SET);
	fwrite(&ler, sizeof(tFifo), 1, file);
}

/*
 * funcao que insere um novo registro na fila
 */
void addFifo(char nomeArquivo[], char entra[], sockaddr_in cliente) {
	FILE *arq_ent;
	tFifo aux;
	short pos, pf, uf;
	
	arq_ent = fopen(nomeArquivo, "r+b");
	strcpy(aux.package, entra);
	aux.addr = cliente;
	aux.prox = 0;
	fseek(arq_ent, 0, SEEK_SET);
	fread(&pf, sizeof(short), 1, arq_ent);
	fread(&uf, sizeof(short), 1, arq_ent);
	fread(&pos, sizeof(short), 1, arq_ent);
	if ((pf == 0) && (uf == 0)) {
		addInicio(aux, arq_ent, pos);		
	}else	if (pos == 0) {
		addFinal(aux, arq_ent);	
	}else {
		addMeio(aux, pos, arq_ent);
	}
	fclose(arq_ent);
	
}

/*
 * funcao que inicializa uma fila
 */
void initFifo(char nomeArquivo[]) {
	FILE *arq_ini;
	short a;	
	a=0;
	arq_ini = fopen(nomeArquivo, "w+b");
	fseek(arq_ini, 0, SEEK_SET);
	fwrite(&a, sizeof(short), 1, arq_ini);
	fwrite(&a, sizeof(short), 1, arq_ini);
	fwrite(&a, sizeof(short), 1, arq_ini);
	fclose(arq_ini);
}

/*
 * funcao que remove um registro na fila
 */
tFifo *removeFifo(char nomeArquivo[]) {
	FILE *arq_ent;
	short prim, np, ar, par, ult;
	tFifo *out = (tFifo *) malloc(sizeof(tFifo)), ler, ler1;

	arq_ent = fopen(nomeArquivo, "r+b");
	fseek(arq_ent, 0, SEEK_SET);
	fread(&prim, sizeof(short), 1, arq_ent);
	fread(&ult, sizeof(short), 1 , arq_ent);
	if (prim != 0) {
		if (ult == prim) {
			fseek(arq_ent, 2, SEEK_SET);
			ar = 0;
			fwrite(&ar, sizeof(short), 1, arq_ent);
		}
		ar = prim;
		prim = calcBs(prim);
		fseek(arq_ent, prim, SEEK_SET);
		fread(&ler, sizeof(tFifo), 1, arq_ent);
		np = ler.prox;
		fseek(arq_ent, 0, SEEK_SET);
		fwrite(&np, sizeof(short), 1, arq_ent);
		*out = ler;
		fseek(arq_ent, 4, SEEK_SET);
		fread(&par, sizeof(short), 1, arq_ent);
		fseek(arq_ent, 4, SEEK_SET);
		fwrite(&ar, sizeof(short), 1, arq_ent);
		ar = calcBs(ar);
		fseek(arq_ent, ar, SEEK_SET);
		ler.prox = par;
		fwrite(&ler, sizeof(tFifo), 1, arq_ent);
		fclose(arq_ent);
		return(out);
	}else {
		fclose(arq_ent);
		return(NULL);
	}
}